/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.ui.dialogs;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.eclipse.emf.common.util.EList;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.uml2.uml.Behavior;
import org.eclipse.uml2.uml.BehavioredClassifier;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.ObjectNode;
import org.eclipse.uml2.uml.State;
import org.eclipse.uml2.uml.Type;

import com.cea.papyrus.core.PapyrusPlugin;

/**
 * Dialog that display a window to select the state of a statemachine
 *
 * @author Gilles Cannenterre
 * @since 12 avr. 07
 */
public class ChooseSetStateDialog extends ChooseSetAssistedDialog implements IChooseDialog {

	/**
	 * The qualified label provider.
	 */
	final private StateQualifiedLabelProvider qualifiedLabelProvider = new StateQualifiedLabelProvider();
	 
	/**
	 * The old subjects.
	 */
	final private EList oldStates;
	
	/**
	 * The uml element.
	 */
	final private Element umlElement;
	
	/**
	 * The has changed.
	 */
	private boolean hasChanged = false; 
	
	/**
	 * The Constructor.
	 * 
	 * @param parentShell the parent shell
	 * @param theElement the the element
	 */
	public ChooseSetStateDialog(Shell parentShell, Element theElement){
		super(parentShell,"Possible States: ","ObjectNode's States: ");
		umlElement = theElement;
		
		labelProvider = new StateLabelProvider();
		decoratedContentProposalProvider = new StateContentProposalProvider();
	
		// save old list of states (used to compare when ok is pressed)
		oldStates = ((ObjectNode)theElement).getInStates();
		
		Iterator<State> state = oldStates.iterator();
		while (state.hasNext()){
			selectedElementList.addElement(state.next());
		}
		
		// build possible list
		ArrayList<State> possibleStates = new ArrayList<State>();
		
		Type type = ((ObjectNode)theElement).getType();
		if ((type != null)&&(type instanceof BehavioredClassifier)) {
			Iterator itBehavior = ((BehavioredClassifier)type).getOwnedBehaviors().iterator();
			while (itBehavior.hasNext()){
				Behavior behavior = (Behavior)itBehavior.next();
				if (behavior instanceof org.eclipse.uml2.uml.StateMachine){
					com.cea.papyrus.umlutils.StateMachine stateMachine = new com.cea.papyrus.umlutils.StateMachine((org.eclipse.uml2.uml.StateMachine)behavior);
					possibleStates.addAll(stateMachine.getAllStates());
				}
			}
			
		}
		state = possibleStates.iterator();		
		while (state.hasNext()){
			State current = (State)state.next();
			if(!selectedElementList.contains(current)) {
				possibleElementList.addElement(current);
			}
		}
	}
	
	/**
	 * Creates the dialog area.
	 * 
	 * @param parent the parent
	 * 
	 * @return the control
	 */
	@Override
	protected Control createDialogArea(Composite parent) {
		Control composite = super.createDialogArea(parent);
		
		// Add 2 columns to the table area
		// possibleElementsTable.setLinesVisible(true);
		possibleElementsTable.setHeaderVisible(true);

		// 1st column with image/checkboxes - NOTE: The SWT.CENTER has no effect!!
		TableColumn column = new TableColumn(possibleElementsTable, SWT.CENTER, 0);		
		column.setText("State");
		column.setWidth(150);
		column.addSelectionListener(new SelectionAdapter() {
	       	
			@Override
			public void widgetSelected(SelectionEvent e) {
				possibleElementsTableViewer.setSorter(new AlphabeticalViewerSorter(0));
			}
		});
		
		// 2nd column with task Description
		column = new TableColumn(possibleElementsTable, SWT.LEFT, 1);
		column.setText("Information");
		column.setWidth(165);
		// Add listener to column so tasks are sorted by description when clicked 
		column.addSelectionListener(new SelectionAdapter() {
       	
			@Override
			public void widgetSelected(SelectionEvent e) {
				possibleElementsTableViewer.setSorter(new AlphabeticalViewerSorter(1));
			}
		});

		// set sorter to the possible element table viewer 
		possibleElementsTableViewer.setSorter(new AlphabeticalViewerSorter(0));
		
		return composite;
	}

	/**
	 * Run add element.
	 * 
	 * @param name the name
	 */
	@Override
	protected void runAddElement(String name) {
		// find the state point in the list
		State state =null;
		Iterator<State> it = possibleElementList.getElements().iterator();
		while (it.hasNext()) {
			State element = (State) it.next();
			if( name.equalsIgnoreCase(element.getName()) || name.equalsIgnoreCase(element.getQualifiedName())) {
				state = element;
			}
		}
		if(state != null) {
			runActionAdd(state);
		}
	}

	/**
	 * Checks if is selectable element.
	 * 
	 * @param text the text
	 * 
	 * @return true, if is selectable element
	 */
	@Override
	protected boolean isSelectableElement(String text) {
		// iterate through all possibilities and return true if text corresponds
		Iterator<State> it = possibleElementList.getElements().iterator();
		while (it.hasNext()) {
			State element = (State) it.next();
			if( text.equalsIgnoreCase(element.getName()) || text.equalsIgnoreCase(element.getQualifiedName())) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Ok pressed.
	 */
	@Override
	protected void okPressed() {
		int modifCount = 0;
		
		// apply new state (diff old and new List)
		modifCount = addStates();
		
		// removed unused states
		modifCount += removeStates();
		
		// order the list of select states
		modifCount += orderStates();
		
		// element has changed if there is a modification or more
		hasChanged = (modifCount > 0);
		
		super.okPressed();
	}

	/**
	 * Adds the subjects.
	 * 
	 * @return the int
	 */
	private int addStates() {
		int modifCount=0;	// count number of creations (to force refresh as dialog is closed)
		// for all element of the new List that is not in the old list, add the extension point in the extension location list
		Iterator newElements = selectedElementList.getElements().iterator();
		while(newElements.hasNext()) {
			State state = (State) newElements.next();
			if(!oldStates.contains(state)) {
				oldStates.add(state);
				modifCount++;
			}
		}
		return modifCount;
	}
	
	/**
	 * Removes the subjects.
	 * 
	 * @return the int
	 */
	private int removeStates() {
		int modifCount=0;	// count number of destructions (to force refresh as dialog is closed)
		// for all element of the old List that is not in the new list, remove the extension 
		// point from the extension location list
		ArrayList<State> elementsToDestroy= new ArrayList<State>();
		Iterator oldElements = oldStates.iterator();
		while(oldElements.hasNext()) {
			State state = (State) oldElements.next();
			if(!selectedElementList.getElements().contains(state)) {
				elementsToDestroy.add(state);
				modifCount++;
			}
		}	
		// apply modifications
		Iterator elementToDestroy = elementsToDestroy.iterator();
		while(elementToDestroy.hasNext()) {
			State state = (State) elementToDestroy.next();
			oldStates.remove(state);
		}
		return modifCount;
	}

	/**
	 * Order subjects.
	 * 
	 * @return the int
	 */
	private int orderStates() {
		int modifCount=0;	// count number of destructions (to force refresh as dialog is closed)	
		// for all element of the old List that is not in the new list, remove the extension 
		// point from the extension location list
		Map<State, Integer> elementsToMove = new HashMap<State, Integer>();
		Iterator it = oldStates.iterator();
		while(it.hasNext()) {
			// position of the state in the new list
			State state = (State) it.next();
			int newIndex = selectedElementList.getElements().indexOf(state);
			if(oldStates.indexOf(state) != newIndex) {
				// state should be moved
				elementsToMove.put(state, new Integer(newIndex));
				modifCount++;
			}
		}
		// apply modifications
		Iterator elementToMove = elementsToMove.keySet().iterator();
		while(elementToMove.hasNext()) {
			State state = (State) elementToMove.next();
			oldStates.move(((Integer)(elementsToMove.get(state))).intValue(), state);;
		}
		return modifCount;
	}

	/**
	 * The Class ClassifierContentProposalProvider.
	 */
	public class StateContentProposalProvider extends DecoratedContentProposalProvider {

		/**
		 * Gets the proposals.
		 * 
		 * @param contents the contents
		 * @param position the position
		 * 
		 * @return the proposals
		 */
		@Override
		public DecoratedContentProposal[] getProposals(String contents, int position) {
			ArrayList<DecoratedContentProposal> proposals = new ArrayList<DecoratedContentProposal>();

			if(possibleElementList != null) {
				Iterator it = possibleElementList.getElements().iterator();
				while(it.hasNext()) {
					final State state = (State)it.next();
					final String simpleName = state.getName();
					final String qualifiedName = state.getQualifiedName();

					if( position < simpleName.length() && contents.substring(0, position).equalsIgnoreCase(simpleName.substring(0, position))) {
						proposals.add(new DecoratedContentProposal(state, labelProvider));
					}

					if( position < qualifiedName.length() && contents.substring(0, position).equalsIgnoreCase(qualifiedName.substring(0, position))) {
						proposals.add(new DecoratedContentProposal(state, qualifiedLabelProvider));
					}
				}
			}
			
			Collections.sort(proposals);
			return proposals.toArray(new DecoratedContentProposal[proposals.size()]);
		}
	}

	/**
	 * The Class ClassifierLabelProvider.
	 */
	protected class StateLabelProvider extends LabelProvider implements ITableLabelProvider {

		/**
		 * Gets the image.
		 * 
		 * @param element the element
		 * 
		 * @return the image
		 */
		@Override
		public Image getImage(Object element) {
			Image image = null;
			if((element instanceof State)) {
				image = PapyrusPlugin.getIconElement((State)element);
			}
			// return the image if not null or the default stereotype image if stereotype has no image
			return (image != null) ? image : PapyrusPlugin.UML_LABEL_PROVIDER.getImage(org.eclipse.uml2.uml.State.class, null) ;
		}

		/**
		 * Gets the text.
		 * 
		 * @param state the classifier
		 * 
		 * @return the text
		 */
		@Override
		public String getText(Object state) {
			if(!(state instanceof State)) {
				return "not a state";
			} else {
				return ((State)state).getName();
			}
		}

		/**
		 * Gets the column image.
		 * 
		 * @param element the element
		 * @param columnIndex the column index
		 * 
		 * @return the column image
		 */
		public Image getColumnImage(Object element, int columnIndex) {
			if(columnIndex == 0) {
				return getImage(element);
			}
			return null;
		}

		/**
		 * Gets the column text.
		 * 
		 * @param element the element
		 * @param columnIndex the column index
		 * 
		 * @return the column text
		 */
		public String getColumnText(Object element, int columnIndex) {
			if(columnIndex == 0) {
				return getText(element);
			} else if(columnIndex == 1) {
				return ((State)element).getQualifiedName();
			}
			return "not the right column index";
		}
	}

	/**
	 * The Class ClassifierQualifiedLabelProvider.
	 */
	protected class StateQualifiedLabelProvider extends StateLabelProvider {
		
		/**
		 * Gets the text.
		 * 
		 * @param state the classifier
		 * 
		 * @return the text
		 */
		@Override
		public String getText(Object state) {
			if(!(state instanceof State)) {
				return "not a state";
			} else {
				return ((State)state).getQualifiedName();
			}
		}
	}

	/**
	 * Checks for element changed.
	 * 
	 * @return true, if has element changed
	 */
	public boolean hasElementChanged() {
		return hasChanged;
	}
}